From 0961f4877fc900a5709217943a556b3696373412 Mon Sep 17 00:00:00 2001 From: Sven Rebhan Date: Thu, 15 Feb 2024 14:40:36 +0100 Subject: [PATCH 01/11] Add unit-tests for address sanitization --- plugins/inputs/postgresql/postgresql_test.go | 135 +++++++++++++++++++ 1 file changed, 135 insertions(+) diff --git a/plugins/inputs/postgresql/postgresql_test.go b/plugins/inputs/postgresql/postgresql_test.go index fa54096edda61..b8b6d48f4b56b 100644 --- a/plugins/inputs/postgresql/postgresql_test.go +++ b/plugins/inputs/postgresql/postgresql_test.go @@ -2,6 +2,7 @@ package postgresql import ( "fmt" + "strings" "testing" "github.com/docker/go-connections/nat" @@ -316,3 +317,137 @@ func TestPostgresqlDatabaseBlacklistTestIntegration(t *testing.T) { require.False(t, foundTemplate0) require.True(t, foundTemplate1) } + +func TestSanitizeAddressKeyValue(t *testing.T) { + keys := []string{"password", "sslcert", "sslkey", "sslmode", "sslrootcert"} + tests := []struct { + name string + value string + }{ + { + name: "simple text", + value: `foo`, + }, + { + name: "empty values", + value: `''`, + }, + { + name: "space in value", + value: `'foo bar'`, + }, + { + name: "escaped quote", + value: `'foo\'s bar'`, + }, + { + name: "escaped quote no space", + value: `\'foobar\'s\'`, + }, + { + name: "escaped backslash", + value: `'foo bar\\'`, + }, + { + name: "escaped quote and backslash", + value: `'foo\\\'s bar'`, + }, + { + name: "two escaped backslashes", + value: `'foo bar\\\\'`, + }, + } + + for _, tt := range tests { + // Key value without spaces around equal sign + t.Run(tt.name, func(t *testing.T) { + // Generate the DSN from the given keys and value + parts := make([]string, 0, len(keys)) + for _, k := range keys { + parts = append(parts, k+"="+tt.value) + } + dsn := strings.Join(parts, " canary=ok ") + + plugin := &Postgresql{ + Service: Service{ + Address: config.NewSecret([]byte(dsn)), + }, + } + + expected := strings.Join(make([]string, len(keys)), "canary=ok ") + actual, err := plugin.SanitizedAddress() + require.NoError(t, err) + require.Equalf(t, expected, actual, "initial: %s", dsn) + }) + + // Key value with spaces around equal sign + t.Run("spaced "+tt.name, func(t *testing.T) { + // Generate the DSN from the given keys and value + parts := make([]string, 0, len(keys)) + for _, k := range keys { + parts = append(parts, k+" = "+tt.value) + } + dsn := strings.Join(parts, " canary=ok ") + + plugin := &Postgresql{ + Service: Service{ + Address: config.NewSecret([]byte(dsn)), + }, + } + + expected := strings.Join(make([]string, len(keys)), "canary=ok ") + actual, err := plugin.SanitizedAddress() + require.NoError(t, err) + require.Equalf(t, expected, actual, "initial: %s", dsn) + }) + } +} + +func TestSanitizeAddressURI(t *testing.T) { + keys := []string{"password", "sslcert", "sslkey", "sslmode", "sslrootcert"} + tests := []struct { + name string + value string + }{ + { + name: "simple text", + value: `foo`, + }, + { + name: "empty values", + value: ``, + }, + { + name: "space in value", + value: `foo bar`, + }, + { + name: "equal sign in value", + value: `foo=bar`, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Generate the DSN from the given keys and value + value := strings.ReplaceAll(tt.value, "=", "%3D") + value = strings.ReplaceAll(value, " ", "%20") + parts := make([]string, 0, len(keys)) + for _, k := range keys { + parts = append(parts, k+"="+value) + } + dsn := "postgresql://user:passwd@localhost:5432/db?" + strings.Join(parts, "&") + + plugin := &Postgresql{ + Service: Service{ + Address: config.NewSecret([]byte(dsn)), + }, + } + + expected := "dbname=db host=localhost port=5432 user=user" + actual, err := plugin.SanitizedAddress() + require.NoError(t, err) + require.Equalf(t, expected, actual, "initial: %s", dsn) + }) + } +} From fd69abda57fe8a83e57eace96f5ce4ac2e7f83a6 Mon Sep 17 00:00:00 2001 From: Sven Rebhan Date: Thu, 15 Feb 2024 14:43:11 +0100 Subject: [PATCH 02/11] Improve regexp to handle spaces, quoted values and escaped characters --- plugins/inputs/postgresql/postgresql_test.go | 2 ++ plugins/inputs/postgresql/service.go | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/plugins/inputs/postgresql/postgresql_test.go b/plugins/inputs/postgresql/postgresql_test.go index b8b6d48f4b56b..4bc3665063f5f 100644 --- a/plugins/inputs/postgresql/postgresql_test.go +++ b/plugins/inputs/postgresql/postgresql_test.go @@ -375,6 +375,7 @@ func TestSanitizeAddressKeyValue(t *testing.T) { } expected := strings.Join(make([]string, len(keys)), "canary=ok ") + expected = strings.TrimSpace(expected) actual, err := plugin.SanitizedAddress() require.NoError(t, err) require.Equalf(t, expected, actual, "initial: %s", dsn) @@ -396,6 +397,7 @@ func TestSanitizeAddressKeyValue(t *testing.T) { } expected := strings.Join(make([]string, len(keys)), "canary=ok ") + expected = strings.TrimSpace(expected) actual, err := plugin.SanitizedAddress() require.NoError(t, err) require.Equalf(t, expected, actual, "initial: %s", dsn) diff --git a/plugins/inputs/postgresql/service.go b/plugins/inputs/postgresql/service.go index 6c76d76f32d82..6acc734c90f39 100644 --- a/plugins/inputs/postgresql/service.go +++ b/plugins/inputs/postgresql/service.go @@ -148,7 +148,7 @@ func (p *Service) Stop() { p.DB.Close() } -var kvMatcher, _ = regexp.Compile(`(password|sslcert|sslkey|sslmode|sslrootcert)=\S+ ?`) +var keyValueMatcher = regexp.MustCompile(`(\s|^)((?:password|sslcert|sslkey|sslmode|sslrootcert)\s?=\s?(?:(?:'(?:[^'\\]|\\.)*')|(?:\S+)))(?:\s|$)`) // SanitizedAddress utility function to strip sensitive information from the connection string. func (p *Service) SanitizedAddress() (sanitizedAddress string, err error) { @@ -171,7 +171,7 @@ func (p *Service) SanitizedAddress() (sanitizedAddress string, err error) { canonicalizedAddress = addr.String() } - return kvMatcher.ReplaceAllString(canonicalizedAddress, ""), nil + return keyValueMatcher.ReplaceAllString(canonicalizedAddress, "$1"), nil } // GetConnectDatabase utility function for getting the database to which the connection was made From c07507cf1de3fa1e537b41f7dc5717c959a6bc1a Mon Sep 17 00:00:00 2001 From: Sven Rebhan Date: Thu, 15 Feb 2024 15:05:52 +0100 Subject: [PATCH 03/11] Improve handling of separator space --- plugins/inputs/postgresql/service.go | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/plugins/inputs/postgresql/service.go b/plugins/inputs/postgresql/service.go index 6acc734c90f39..77882eacbeae3 100644 --- a/plugins/inputs/postgresql/service.go +++ b/plugins/inputs/postgresql/service.go @@ -148,30 +148,32 @@ func (p *Service) Stop() { p.DB.Close() } -var keyValueMatcher = regexp.MustCompile(`(\s|^)((?:password|sslcert|sslkey|sslmode|sslrootcert)\s?=\s?(?:(?:'(?:[^'\\]|\\.)*')|(?:\S+)))(?:\s|$)`) +var sanitizer = regexp.MustCompile(`(\s|^)((?:password|sslcert|sslkey|sslmode|sslrootcert)\s?=\s?(?:(?:'(?:[^'\\]|\\.)*')|(?:\S+)))`) // SanitizedAddress utility function to strip sensitive information from the connection string. -func (p *Service) SanitizedAddress() (sanitizedAddress string, err error) { +func (p *Service) SanitizedAddress() (string, error) { if p.OutputAddress != "" { return p.OutputAddress, nil } - addr, err := p.Address.Get() + // Get the address + addrSecret, err := p.Address.Get() if err != nil { - return sanitizedAddress, fmt.Errorf("getting address for sanitization failed: %w", err) + return "", fmt.Errorf("getting address for sanitization failed: %w", err) } - defer addr.Destroy() + addr := addrSecret.String() + addrSecret.Destroy() - var canonicalizedAddress string - if strings.HasPrefix(addr.TemporaryString(), "postgres://") || strings.HasPrefix(addr.TemporaryString(), "postgresql://") { - if canonicalizedAddress, err = parseURL(addr.String()); err != nil { - return sanitizedAddress, err + // Make sure we convert URI-formatted strings into key-values + if strings.HasPrefix(addr, "postgres://") || strings.HasPrefix(addr, "postgresql://") { + if addr, err = parseURL(addr); err != nil { + return "", err } - } else { - canonicalizedAddress = addr.String() } - return keyValueMatcher.ReplaceAllString(canonicalizedAddress, "$1"), nil + // Sanitize the string using a regular expression + sanitized := sanitizer.ReplaceAllString(addr, "") + return strings.TrimSpace(sanitized), nil } // GetConnectDatabase utility function for getting the database to which the connection was made From d84dfa8609860a0f35532a9f3ad3e17120697db1 Mon Sep 17 00:00:00 2001 From: Sven Rebhan Date: Thu, 15 Feb 2024 15:06:47 +0100 Subject: [PATCH 04/11] Add test for equal sign --- plugins/inputs/postgresql/postgresql_test.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/plugins/inputs/postgresql/postgresql_test.go b/plugins/inputs/postgresql/postgresql_test.go index 4bc3665063f5f..0f6707468b87f 100644 --- a/plugins/inputs/postgresql/postgresql_test.go +++ b/plugins/inputs/postgresql/postgresql_test.go @@ -336,6 +336,10 @@ func TestSanitizeAddressKeyValue(t *testing.T) { name: "space in value", value: `'foo bar'`, }, + { + name: "equal sign in value", + value: `'foo=bar'`, + }, { name: "escaped quote", value: `'foo\'s bar'`, From 15abb6c98eacb975067fe69c8927df82fa02b4ff Mon Sep 17 00:00:00 2001 From: Sven Rebhan Date: Thu, 15 Feb 2024 15:48:09 +0100 Subject: [PATCH 05/11] Add test for URI parsing --- plugins/inputs/postgresql/postgresql_test.go | 68 ++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/plugins/inputs/postgresql/postgresql_test.go b/plugins/inputs/postgresql/postgresql_test.go index 0f6707468b87f..6558b4229bc85 100644 --- a/plugins/inputs/postgresql/postgresql_test.go +++ b/plugins/inputs/postgresql/postgresql_test.go @@ -318,6 +318,74 @@ func TestPostgresqlDatabaseBlacklistTestIntegration(t *testing.T) { require.True(t, foundTemplate1) } +func TestURIParsing(t *testing.T) { + tests := []struct { + name string + uri string + expected string + }{ + { + name: "short", + uri: `postgres://localhost`, + expected: "host=localhost", + }, + { + name: "with port", + uri: `postgres://localhost:5432`, + expected: "host=localhost port=5432", + }, + { + name: "with database", + uri: `postgres://localhost/mydb`, + expected: "dbname=mydb host=localhost", + }, + { + name: "with additional parameters", + uri: `postgres://localhost/mydb?application_name=pgxtest&search_path=myschema&connect_timeout=5`, + expected: "application_name=pgxtest connect_timeout=5 dbname=mydb host=localhost search_path=myschema", + }, + { + name: "with database setting in params", + uri: `postgres://localhost:5432/?database=mydb`, + expected: "database=mydb host=localhost port=5432", + }, + { + name: "with authentication", + uri: `postgres://jack:secret@localhost:5432/mydb?sslmode=prefer`, + expected: "dbname=mydb host=localhost password=secret port=5432 sslmode=prefer user=jack", + }, + { + name: "with spaces", + uri: `postgres://jack%20hunter:secret@localhost:5432/mydb?application_name=pgx%20test&search_path=myschema&connect_timeout=5`, + expected: "application_name='pgx test' connect_timeout=5 dbname=mydb host=localhost password=secret port=5432 search_path=myschema user='jack hunter'", + }, + { + name: "with equal signs", + uri: `postgres://jack%20hunter:secret@localhost:5432/mydb?application_name=pgx%3Dtest&search_path=myschema&connect_timeout=5`, + expected: "application_name='pgx=test' connect_timeout=5 dbname=mydb host=localhost password=secret port=5432 search_path=myschema user='jack hunter'", + }, + { + name: "multiple hosts", + uri: `postgres://jack:secret@foo:1,bar:2,baz:3/mydb?sslmode=disable`, + expected: "dbname=mydb host=foo,bar,baz password=secret port=1,2,3 sslmode=disable user=jack", + }, + { + name: "multiple hosts without ports", + uri: `postgres://jack:secret@foo,bar,baz/mydb?sslmode=disable`, + expected: "dbname=mydb host=foo,bar,baz password=secret sslmode=disable user=jack", + }, + } + + for _, tt := range tests { + // Key value without spaces around equal sign + t.Run(tt.name, func(t *testing.T) { + actual, err := parseURL(tt.uri) + require.NoError(t, err) + require.Equalf(t, tt.expected, actual, "initial: %s", tt.uri) + }) + } +} + func TestSanitizeAddressKeyValue(t *testing.T) { keys := []string{"password", "sslcert", "sslkey", "sslmode", "sslrootcert"} tests := []struct { From 1cdcf8883a971bc70fad7c5cdb4b81dce5ec96eb Mon Sep 17 00:00:00 2001 From: Sven Rebhan Date: Thu, 15 Feb 2024 16:27:14 +0100 Subject: [PATCH 06/11] Fix URI parsing --- plugins/inputs/postgresql/postgresql_test.go | 2 +- plugins/inputs/postgresql/service.go | 97 ++++++++++---------- 2 files changed, 51 insertions(+), 48 deletions(-) diff --git a/plugins/inputs/postgresql/postgresql_test.go b/plugins/inputs/postgresql/postgresql_test.go index 6558b4229bc85..7ae1e9f1776f6 100644 --- a/plugins/inputs/postgresql/postgresql_test.go +++ b/plugins/inputs/postgresql/postgresql_test.go @@ -379,7 +379,7 @@ func TestURIParsing(t *testing.T) { for _, tt := range tests { // Key value without spaces around equal sign t.Run(tt.name, func(t *testing.T) { - actual, err := parseURL(tt.uri) + actual, err := toKeyValue(tt.uri) require.NoError(t, err) require.Equalf(t, tt.expected, actual, "initial: %s", tt.uri) }) diff --git a/plugins/inputs/postgresql/service.go b/plugins/inputs/postgresql/service.go index 77882eacbeae3..810fb74582ad5 100644 --- a/plugins/inputs/postgresql/service.go +++ b/plugins/inputs/postgresql/service.go @@ -17,72 +17,75 @@ import ( "github.com/influxdata/telegraf/config" ) -// pulled from lib/pq -// ParseURL no longer needs to be used by clients of this library since supplying a URL as a -// connection string to sql.Open() is now supported: -// -// sql.Open("postgres", "postgres://bob:secret@1.2.3.4:5432/mydb?sslmode=verify-full") -// -// It remains exported here for backwards-compatibility. -// -// ParseURL converts a url to a connection string for driver.Open. -// Example: -// -// "postgres://bob:secret@1.2.3.4:5432/mydb?sslmode=verify-full" -// -// converts to: -// -// "user=bob password=secret host=1.2.3.4 port=5432 dbname=mydb sslmode=verify-full" -// -// A minimal example: -// -// "postgres://" -// -// This will be blank, causing driver.Open to use all of the defaults -func parseURL(uri string) (string, error) { +// Based on parseURLSettings() at https://github.com/jackc/pgx/blob/master/pgconn/config.go +func toKeyValue(uri string) (string, error) { u, err := url.Parse(uri) if err != nil { - return "", err + return "", fmt.Errorf("parsing URI failed: %w", err) } + // Check the protocol if u.Scheme != "postgres" && u.Scheme != "postgresql" { return "", fmt.Errorf("invalid connection protocol: %s", u.Scheme) } - var kvs []string - escaper := strings.NewReplacer(` `, `\ `, `'`, `\'`, `\`, `\\`) - accrue := func(k, v string) { - if v != "" { - kvs = append(kvs, k+"="+escaper.Replace(v)) + quoteIfNecessary := func(v string) string { + if !strings.ContainsAny(v, ` ='\`) { + return v } + r := strings.ReplaceAll(v, `\`, `\\`) + r = strings.ReplaceAll(r, `'`, `\'`) + return "'" + r + "'" } + // Extract the parameters + var parts []string if u.User != nil { - v := u.User.Username() - accrue("user", v) - - v, _ = u.User.Password() - accrue("password", v) + parts = append(parts, "user="+quoteIfNecessary(u.User.Username())) + if password, found := u.User.Password(); found { + parts = append(parts, "password="+quoteIfNecessary(password)) + } } - if host, port, err := net.SplitHostPort(u.Host); err != nil { - accrue("host", u.Host) - } else { - accrue("host", host) - accrue("port", port) + // Handle multiple host:port's in url.Host by splitting them into host,host,host and port,port,port. + var hosts []string + var ports []string + var anyPortSet bool + for _, host := range strings.Split(u.Host, ",") { + if host == "" { + continue + } + + h, p, err := net.SplitHostPort(host) + if err != nil { + if !strings.Contains(err.Error(), "missing port") { + return "", fmt.Errorf("failed to process host %q: %w", host, err) + } + h = host + } + anyPortSet = anyPortSet || err == nil + hosts = append(hosts, h) + ports = append(ports, p) + } + if len(hosts) > 0 { + parts = append(parts, "host="+strings.Join(hosts, ",")) + } + if anyPortSet { + parts = append(parts, "port="+strings.Join(ports, ",")) } - if u.Path != "" { - accrue("dbname", u.Path[1:]) + database := strings.TrimLeft(u.Path, "/") + if database != "" { + parts = append(parts, "dbname="+quoteIfNecessary(database)) } - q := u.Query() - for k := range q { - accrue(k, q.Get(k)) + for k, v := range u.Query() { + parts = append(parts, k+"="+quoteIfNecessary(strings.Join(v, ","))) } - sort.Strings(kvs) // Makes testing easier (not a performance concern) - return strings.Join(kvs, " "), nil + // Required to produce a repeatable output e.g. for tags or testing + sort.Strings(parts) + return strings.Join(parts, " "), nil } // Service common functionality shared between the postgresql and postgresql_extensible @@ -166,7 +169,7 @@ func (p *Service) SanitizedAddress() (string, error) { // Make sure we convert URI-formatted strings into key-values if strings.HasPrefix(addr, "postgres://") || strings.HasPrefix(addr, "postgresql://") { - if addr, err = parseURL(addr); err != nil { + if addr, err = toKeyValue(addr); err != nil { return "", err } } From dd9da2b240932a0319ad9b35338458ae87e8929f Mon Sep 17 00:00:00 2001 From: Sven Rebhan Date: Thu, 15 Feb 2024 16:54:44 +0100 Subject: [PATCH 07/11] Make linter happy --- plugins/inputs/postgresql/postgresql_test.go | 8 ++++---- plugins/inputs/postgresql/service.go | 9 +++++---- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/plugins/inputs/postgresql/postgresql_test.go b/plugins/inputs/postgresql/postgresql_test.go index 7ae1e9f1776f6..c5d737488a700 100644 --- a/plugins/inputs/postgresql/postgresql_test.go +++ b/plugins/inputs/postgresql/postgresql_test.go @@ -356,13 +356,13 @@ func TestURIParsing(t *testing.T) { }, { name: "with spaces", - uri: `postgres://jack%20hunter:secret@localhost:5432/mydb?application_name=pgx%20test&search_path=myschema&connect_timeout=5`, - expected: "application_name='pgx test' connect_timeout=5 dbname=mydb host=localhost password=secret port=5432 search_path=myschema user='jack hunter'", + uri: `postgres://jack%20hunter:secret@localhost/mydb?application_name=pgx%20test`, + expected: "application_name='pgx test' dbname=mydb host=localhost password=secret user='jack hunter'", }, { name: "with equal signs", - uri: `postgres://jack%20hunter:secret@localhost:5432/mydb?application_name=pgx%3Dtest&search_path=myschema&connect_timeout=5`, - expected: "application_name='pgx=test' connect_timeout=5 dbname=mydb host=localhost password=secret port=5432 search_path=myschema user='jack hunter'", + uri: `postgres://jack%20hunter:secret@localhost/mydb?application_name=pgx%3Dtest`, + expected: "application_name='pgx=test' dbname=mydb host=localhost password=secret user='jack hunter'", }, { name: "multiple hosts", diff --git a/plugins/inputs/postgresql/service.go b/plugins/inputs/postgresql/service.go index 810fb74582ad5..e06fbbaf89e8e 100644 --- a/plugins/inputs/postgresql/service.go +++ b/plugins/inputs/postgresql/service.go @@ -39,7 +39,7 @@ func toKeyValue(uri string) (string, error) { } // Extract the parameters - var parts []string + parts := make([]string, 0, len(u.Query())+5) if u.User != nil { parts = append(parts, "user="+quoteIfNecessary(u.User.Username())) if password, found := u.User.Password(); found { @@ -48,10 +48,11 @@ func toKeyValue(uri string) (string, error) { } // Handle multiple host:port's in url.Host by splitting them into host,host,host and port,port,port. - var hosts []string - var ports []string + hostParts := strings.Split(u.Host, ",") + hosts := make([]string, 0, len(hostParts)) + ports := make([]string, 0, len(hostParts)) var anyPortSet bool - for _, host := range strings.Split(u.Host, ",") { + for _, host := range hostParts { if host == "" { continue } From 5bd3caa6c3f43171ecd210c0dded80a3a06f88f4 Mon Sep 17 00:00:00 2001 From: Sven Rebhan Date: Mon, 19 Feb 2024 10:27:32 +0100 Subject: [PATCH 08/11] Document potential tag-value change --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bb16492d7c1fd..7a46f9439acbb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -72,6 +72,9 @@ - [#14771](https://github.com/influxdata/telegraf/pull/14771) `deps` Bump tj-actions/changed-files from 41 to 42 - [#14757](https://github.com/influxdata/telegraf/pull/14757) `deps` Get rid of golang.org/x/exp and use stable versions instead - [#14753](https://github.com/influxdata/telegraf/pull/14753) `deps` Use github.com/coreos/go-systemd/v22 instead of git version +- With correctly sanitizing PostgreSQL addresses ([PR #14829](https://github.com/influxdata/telegraf/pull/14829)) + the `server` tag value for a URI-format address might change in case it + contains spaces, backslashes or single-quotes in non-redacted parameters. ## v1.29.4 [2024-01-31] From 69186900255f783a6ba5756c5ca9f9dac685df0e Mon Sep 17 00:00:00 2001 From: Sven Rebhan Date: Wed, 28 Feb 2024 21:53:42 +0100 Subject: [PATCH 09/11] Fix rebase fallout --- CHANGELOG.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a46f9439acbb..e2a66533d352c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,9 @@ consecutive messages exceeds the timeout. [#14837](https://github.com/influxdata/telegraf/pull/14828) sets the timeout to infinite (i.e zero) as this is the expected behavior. +- With correctly sanitizing PostgreSQL addresses ([PR #14829](https://github.com/influxdata/telegraf/pull/14829)) + the `server` tag value for a URI-format address might change in case it + contains spaces, backslashes or single-quotes in non-redacted parameters. ## v1.29.5 [2024-02-20] @@ -72,9 +75,6 @@ - [#14771](https://github.com/influxdata/telegraf/pull/14771) `deps` Bump tj-actions/changed-files from 41 to 42 - [#14757](https://github.com/influxdata/telegraf/pull/14757) `deps` Get rid of golang.org/x/exp and use stable versions instead - [#14753](https://github.com/influxdata/telegraf/pull/14753) `deps` Use github.com/coreos/go-systemd/v22 instead of git version -- With correctly sanitizing PostgreSQL addresses ([PR #14829](https://github.com/influxdata/telegraf/pull/14829)) - the `server` tag value for a URI-format address might change in case it - contains spaces, backslashes or single-quotes in non-redacted parameters. ## v1.29.4 [2024-01-31] From 6f322d31e282ee086aaec2b5dbbb843a34586026 Mon Sep 17 00:00:00 2001 From: Sven Rebhan Date: Wed, 28 Feb 2024 21:58:50 +0100 Subject: [PATCH 10/11] Destroy non-sanitized DSN secret after processing instead of copying it --- plugins/inputs/postgresql/service.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/inputs/postgresql/service.go b/plugins/inputs/postgresql/service.go index e06fbbaf89e8e..d9447b4d3e019 100644 --- a/plugins/inputs/postgresql/service.go +++ b/plugins/inputs/postgresql/service.go @@ -165,10 +165,10 @@ func (p *Service) SanitizedAddress() (string, error) { if err != nil { return "", fmt.Errorf("getting address for sanitization failed: %w", err) } - addr := addrSecret.String() - addrSecret.Destroy() + defer addrSecret.Destroy() // Make sure we convert URI-formatted strings into key-values + addr := addrSecret.TemporaryString() if strings.HasPrefix(addr, "postgres://") || strings.HasPrefix(addr, "postgresql://") { if addr, err = toKeyValue(addr); err != nil { return "", err From 432a8559dcf6f2be7026c11e7f3a8b69a746bb6e Mon Sep 17 00:00:00 2001 From: Sven Rebhan Date: Wed, 28 Feb 2024 22:03:09 +0100 Subject: [PATCH 11/11] Add more test-cases as suggested in review --- plugins/inputs/postgresql/postgresql_test.go | 28 ++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/plugins/inputs/postgresql/postgresql_test.go b/plugins/inputs/postgresql/postgresql_test.go index c5d737488a700..dfb5da0d21cda 100644 --- a/plugins/inputs/postgresql/postgresql_test.go +++ b/plugins/inputs/postgresql/postgresql_test.go @@ -428,6 +428,34 @@ func TestSanitizeAddressKeyValue(t *testing.T) { name: "two escaped backslashes", value: `'foo bar\\\\'`, }, + { + name: "multiple inline spaces", + value: "'foo \t bar'", + }, + { + name: "leading space", + value: `' foo bar'`, + }, + { + name: "trailing space", + value: `'foo bar '`, + }, + { + name: "multiple equal signs", + value: `'foo===bar'`, + }, + { + name: "leading equal sign", + value: `'=foo bar'`, + }, + { + name: "trailing equal sign", + value: `'foo bar='`, + }, + { + name: "mix of equal signs and spaces", + value: "'foo = a\t===\tbar'", + }, } for _, tt := range tests {