-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Redis PubSub Support #2610
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: development
Are you sure you want to change the base?
Redis PubSub Support #2610
Conversation
| } | ||
|
|
||
| if r.logger != nil { | ||
| r.logger.Debugf("connecting to Redis at '%s'", r.cfg.Addr) |
Check failure
Code scanning / CodeQL
Clear-text logging of sensitive information High
Sensitive data returned by an access to password
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 6 days ago
To fix the problem, we should prevent sensitive information such as credentials from being logged in plaintext. Specifically, before logging the Redis address (r.cfg.Addr), we should sanitize it to remove any embedded credentials if they exist, or log only the host/port portion if that's all that's needed for troubleshooting.
- In
pkg/gofr/datasource/pubsub/redis/redis.go, modify the log statements that includer.cfg.Addrso that ifAddrcontains credentials (recognized by the presence of an@and possibly a user:pass syntax before the host), those are omitted or replaced with a placeholder. This can be achieved by writing a small helper function that parses the address and removes obfuscated credentials if present. - Replace the instances of
r.cfg.Addrin logging calls (Debugf,Errorf,Logfetc.) with calls to this helper sanitizing function. - Add the helper function in the same file (
redis.go), as it needs to be accessible where logging occurs.
-
Copy modified lines R21-R45 -
Copy modified line R147 -
Copy modified line R172 -
Copy modified line R186 -
Copy modified line R191
| @@ -18,6 +18,31 @@ | ||
| "gofr.dev/pkg/gofr/datasource/pubsub" | ||
| ) | ||
|
|
||
| // sanitizeRedisAddr removes credentials from a Redis address if present. | ||
| func sanitizeRedisAddr(addr string) string { | ||
| // Handles Redis URIs with credentials: redis://user:pass@host:port or | ||
| // plain host:port | ||
| if strings.Contains(addr, "@") { | ||
| parts := strings.Split(addr, "@") | ||
| if len(parts) == 2 { | ||
| // Remove credentials part, retain host:port or rest. | ||
| // For URI, could be redis://user:pass@host:port | ||
| // Optionally retain scheme. | ||
| credsPart := parts[0] | ||
| hostPart := parts[1] | ||
| // If there's a scheme, preserve it. | ||
| schemeParts := strings.SplitN(credsPart, "//", 2) | ||
| if len(schemeParts) == 2 { | ||
| return schemeParts[0] + "//" + hostPart | ||
| } | ||
| return hostPart | ||
| } | ||
| // If address has multiple '@', keep right-most part. | ||
| return parts[len(parts)-1] | ||
| } | ||
| return addr | ||
| } | ||
|
|
||
| // Client represents a Redis PubSub client. | ||
| type Client struct { | ||
| // Separate connections for pub/sub operations | ||
| @@ -119,7 +144,7 @@ | ||
| } | ||
|
|
||
| if r.logger != nil { | ||
| r.logger.Debugf("connecting to Redis at '%s'", r.cfg.Addr) | ||
| r.logger.Debugf("connecting to Redis at '%s'", sanitizeRedisAddr(r.cfg.Addr)) | ||
| } | ||
|
|
||
| options, err := createRedisOptions(r.cfg) | ||
| @@ -144,7 +169,7 @@ | ||
|
|
||
| if err := r.testConnections(); err != nil { | ||
| if r.logger != nil { | ||
| r.logger.Errorf("failed to connect to Redis at '%s', error: %v", r.cfg.Addr, err) | ||
| r.logger.Errorf("failed to connect to Redis at '%s', error: %v", sanitizeRedisAddr(r.cfg.Addr), err) | ||
| } | ||
|
|
||
| go r.retryConnect() | ||
| @@ -158,12 +183,12 @@ | ||
|
|
||
| if err := r.queryConn.Ping(ctx).Err(); err != nil { | ||
| if r.logger != nil { | ||
| r.logger.Errorf("failed to connect query connection to Redis at '%s', error: %v", r.cfg.Addr, err) | ||
| r.logger.Errorf("failed to connect query connection to Redis at '%s', error: %v", sanitizeRedisAddr(r.cfg.Addr), err) | ||
| } | ||
| } | ||
|
|
||
| if r.logger != nil { | ||
| r.logger.Logf("connected to Redis at '%s'", r.cfg.Addr) | ||
| r.logger.Logf("connected to Redis at '%s'", sanitizeRedisAddr(r.cfg.Addr)) | ||
| } | ||
| } | ||
|
|
|
|
||
| if err := r.testConnections(); err != nil { | ||
| if r.logger != nil { | ||
| r.logger.Errorf("failed to connect to Redis at '%s', error: %v", r.cfg.Addr, err) |
Check failure
Code scanning / CodeQL
Clear-text logging of sensitive information High
Sensitive data returned by an access to password
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 6 days ago
To fix the issue:
- Do not log sensitive configuration values directly, particularly connection strings that may embed credentials.
- Specifically, update the log messages in
pkg/gofr/datasource/pubsub/redis/redis.gowithin theConnect()method to omitr.cfg.Addrwhen logging errors or connection events. Optionally, if you want to indicate which Redis host is being connected, log only the host/port part after sanitizing the string to remove credentials. However, for safety and generality, it is best to omit or mask the address completely. - Edit any logging statements (lines 122, 147, 161, 166) using
r.cfg.Addrto either remove the address, replace it with a non-sensitive identifier (like "Redis"), or show only the host/port (without credentials) through a helper method that removes any user info from the connection string.
To implement this:
- Edit
Connect()inpkg/gofr/datasource/pubsub/redis/redis.go, replacing log statements that referencer.cfg.Addrto not print it, or to use a sanitized/obfuscated form. - Optionally: If desired, add a helper function (within this file/region) that extracts host and port from a Redis URI (removing any credentials), and use that for logging, though its necessity is debatable.
-
Copy modified line R122 -
Copy modified line R147 -
Copy modified line R161 -
Copy modified line R166
| @@ -119,7 +119,7 @@ | ||
| } | ||
|
|
||
| if r.logger != nil { | ||
| r.logger.Debugf("connecting to Redis at '%s'", r.cfg.Addr) | ||
| r.logger.Debugf("connecting to Redis") | ||
| } | ||
|
|
||
| options, err := createRedisOptions(r.cfg) | ||
| @@ -144,7 +144,7 @@ | ||
|
|
||
| if err := r.testConnections(); err != nil { | ||
| if r.logger != nil { | ||
| r.logger.Errorf("failed to connect to Redis at '%s', error: %v", r.cfg.Addr, err) | ||
| r.logger.Errorf("failed to connect to Redis, error: %v", err) | ||
| } | ||
|
|
||
| go r.retryConnect() | ||
| @@ -158,12 +158,12 @@ | ||
|
|
||
| if err := r.queryConn.Ping(ctx).Err(); err != nil { | ||
| if r.logger != nil { | ||
| r.logger.Errorf("failed to connect query connection to Redis at '%s', error: %v", r.cfg.Addr, err) | ||
| r.logger.Errorf("failed to connect query connection to Redis, error: %v", err) | ||
| } | ||
| } | ||
|
|
||
| if r.logger != nil { | ||
| r.logger.Logf("connected to Redis at '%s'", r.cfg.Addr) | ||
| r.logger.Logf("connected to Redis") | ||
| } | ||
| } | ||
|
|
|
|
||
| if err := r.queryConn.Ping(ctx).Err(); err != nil { | ||
| if r.logger != nil { | ||
| r.logger.Errorf("failed to connect query connection to Redis at '%s', error: %v", r.cfg.Addr, err) |
Check failure
Code scanning / CodeQL
Clear-text logging of sensitive information High
Sensitive data returned by an access to password
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 6 days ago
The best fix is to remove logging of the full address string in log messages and diagnostic errors, both in success and failure cases. Instead, only log non-sensitive details: for example, host and port (if it's certain they're not secret), or replace the address by a generic indication like "[REDACTED]" or "[address hidden]". This fix should apply to all places in pkg/gofr/datasource/pubsub/redis/redis.go where r.cfg.Addr is logged—in error, info, and debug messages. Specifically, lines in Connect() like 122, 147, 161, and 166 should be updated. It is best to avoid logging the address unless certain it cannot contain credentials, but since the taint flow is unclear and credentials can flow into the address, we must treat it as sensitive.
No new methods or imports are needed—replace usages with redacted text or rephrase messages. If there are places where only parts (e.g., host, port) are logged and are confirmed non-sensitive, you may safely log those, but since host/port may also be sensitive in some setups, it is safer to redact completely.
-
Copy modified line R122 -
Copy modified line R147 -
Copy modified line R161 -
Copy modified line R166
| @@ -119,7 +119,7 @@ | ||
| } | ||
|
|
||
| if r.logger != nil { | ||
| r.logger.Debugf("connecting to Redis at '%s'", r.cfg.Addr) | ||
| r.logger.Debugf("connecting to Redis [address hidden]") | ||
| } | ||
|
|
||
| options, err := createRedisOptions(r.cfg) | ||
| @@ -144,7 +144,7 @@ | ||
|
|
||
| if err := r.testConnections(); err != nil { | ||
| if r.logger != nil { | ||
| r.logger.Errorf("failed to connect to Redis at '%s', error: %v", r.cfg.Addr, err) | ||
| r.logger.Errorf("failed to connect to Redis [address hidden], error: %v", err) | ||
| } | ||
|
|
||
| go r.retryConnect() | ||
| @@ -158,12 +158,12 @@ | ||
|
|
||
| if err := r.queryConn.Ping(ctx).Err(); err != nil { | ||
| if r.logger != nil { | ||
| r.logger.Errorf("failed to connect query connection to Redis at '%s', error: %v", r.cfg.Addr, err) | ||
| r.logger.Errorf("failed to connect query connection to Redis [address hidden], error: %v", err) | ||
| } | ||
| } | ||
|
|
||
| if r.logger != nil { | ||
| r.logger.Logf("connected to Redis at '%s'", r.cfg.Addr) | ||
| r.logger.Logf("connected to Redis [address hidden]") | ||
| } | ||
| } | ||
|
|
| } | ||
|
|
||
| if r.logger != nil { | ||
| r.logger.Logf("connected to Redis at '%s'", r.cfg.Addr) |
Check failure
Code scanning / CodeQL
Clear-text logging of sensitive information High
Sensitive data returned by an access to password
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 6 days ago
To safely fix the issue, avoid logging any potentially sensitive information such as host addresses or URIs that may contain embedded credentials. Instead, log only non-sensitive information or redact the sensitive parts before including them in logs.
Specific fix:
- Edit only the logging call on line 166 (and by extension any other similar log calls if found) in
pkg/gofr/datasource/pubsub/redis/redis.go. - Remove the actual address from being logged, or replace it with a redacted/placeholder string (e.g.,
[REDACTED]or log only the host/port after verifying no credentials are present). - No new imports or method definitions are needed for simply redacting or omitting the address.
-
Copy modified line R166
| @@ -163,7 +163,7 @@ | ||
| } | ||
|
|
||
| if r.logger != nil { | ||
| r.logger.Logf("connected to Redis at '%s'", r.cfg.Addr) | ||
| r.logger.Logf("connected to Redis") // Do not log address to avoid leaking sensitive information | ||
| } | ||
| } | ||
|
|
| if pubErr != nil || subErr != nil || queryErr != nil { | ||
| if r.logger != nil { | ||
| r.logger.Errorf("could not connect to Redis at '%s', pub error: %v, sub error: %v, query error: %v", | ||
| r.cfg.Addr, pubErr, subErr, queryErr) |
Check failure
Code scanning / CodeQL
Clear-text logging of sensitive information High
Sensitive data returned by an access to password
| } | ||
|
|
||
| if r.logger != nil { | ||
| r.logger.Logf("reconnected to Redis at '%s'", r.cfg.Addr) |
Check failure
Code scanning / CodeQL
Clear-text logging of sensitive information High
Sensitive data returned by an access to password
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 6 days ago
To safely address the vulnerability, we should ensure that any logging of connection addresses (r.cfg.Addr) omits any embedded credentials. The best way to do this is to sanitize the address before logging it. In practice, Redis connection addresses may be either bare host:port, or a URL containing potential credentials (e.g., redis://user:password@host:port/db). To fix, we should parse the address: if it's a URL, redact the password/user portion before logging; if it's host:port, log directly.
Where to change:
In pkg/gofr/datasource/pubsub/redis/redis.go, at the point of logging (lines like r.logger.Logf("reconnected to Redis at '%s'", r.cfg.Addr) and similar lines like debug/errors), always replace direct use of r.cfg.Addr with a sanitized form (e.g. a helper function such as redactRedisAddr(r.cfg.Addr)).
How to fix:
- Add a helper (e.g.,
redactRedisAddr) in the file to parse addresses, redact password/user if present, and return a safe string for logging. - Replace all logging statements that reference
r.cfg.Addrdirectly with calls using the helper.
What's required:
A new helper function in the file. No new external dependencies are needed, just use Go's standard net/url package to parse and redact credentials in URLs.
-
Copy modified line R12 -
Copy modified lines R47-R49 -
Copy modified line R125 -
Copy modified line R150 -
Copy modified line R240 -
Copy modified line R247
| @@ -9,6 +9,7 @@ | ||
| "strings" | ||
| "sync" | ||
| "time" | ||
| "net/url" | ||
|
|
||
| "github.com/redis/go-redis/v9" | ||
| "go.opentelemetry.io/otel" | ||
| @@ -43,7 +44,9 @@ | ||
| tracer trace.Tracer | ||
| } | ||
|
|
||
| // New creates a new Redis PubSub client. | ||
|
|
||
|
|
||
| // redactRedisAddr removes credentials from a Redis address string for safe logging. | ||
| func New(cfg *Config) *Client { | ||
| if cfg == nil { | ||
| cfg = DefaultConfig() | ||
| @@ -119,7 +122,7 @@ | ||
| } | ||
|
|
||
| if r.logger != nil { | ||
| r.logger.Debugf("connecting to Redis at '%s'", r.cfg.Addr) | ||
| r.logger.Debugf("connecting to Redis at '%s'", redactRedisAddr(r.cfg.Addr)) | ||
| } | ||
|
|
||
| options, err := createRedisOptions(r.cfg) | ||
| @@ -144,7 +147,7 @@ | ||
|
|
||
| if err := r.testConnections(); err != nil { | ||
| if r.logger != nil { | ||
| r.logger.Errorf("failed to connect to Redis at '%s', error: %v", r.cfg.Addr, err) | ||
| r.logger.Errorf("failed to connect to Redis at '%s', error: %v", redactRedisAddr(r.cfg.Addr), err) | ||
| } | ||
|
|
||
| go r.retryConnect() | ||
| @@ -234,14 +237,14 @@ | ||
| if pubErr != nil || subErr != nil || queryErr != nil { | ||
| if r.logger != nil { | ||
| r.logger.Errorf("could not connect to Redis at '%s', pub error: %v, sub error: %v, query error: %v", | ||
| r.cfg.Addr, pubErr, subErr, queryErr) | ||
| redactRedisAddr(r.cfg.Addr), pubErr, subErr, queryErr) | ||
| } | ||
|
|
||
| continue | ||
| } | ||
|
|
||
| if r.logger != nil { | ||
| r.logger.Logf("reconnected to Redis at '%s'", r.cfg.Addr) | ||
| r.logger.Logf("reconnected to Redis at '%s'", redactRedisAddr(r.cfg.Addr)) | ||
| } | ||
|
|
||
| // Restart subscriptions if they were active |
Pull Request Template
Description:
Checklist:
goimportandgolangci-lint.Thank you for your contribution!