Redact sensitive data from trace output#261
Conversation
|
Warning Rate limit exceeded@dzirtusss has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 14 minutes and 23 seconds before requesting another review. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. 📒 Files selected for processing (3)
✨ Finishing touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
6b16a22 to
862c328
Compare
Code Review - PR #261: Redact sensitive data from trace outputOverall AssessmentThis is a solid security improvement that addresses a real risk of accidentally exposing sensitive data in trace output. However, there are critical issues that need to be addressed. Critical Issues1. Missing Return Value in << Method (lib/core/controlplane_api_direct.rb:8) The RedactedDebugOutput#<< method writes to stdout but doesn't return self. This breaks the expected behavior of the << operator. Net::HTTP expects set_debug_output to receive an object where << returns self for method chaining. Fix: Add 'self' as return value in the << method. 2. Overly Broad Redaction Pattern (lib/core/controlplane_api_direct.rb:19) The regex [\w-._]{50,} will redact ANY sequence of 50+ characters including legitimate non-sensitive data like long JSON values, URLs, concatenated UUIDs, etc. Recommendation: Use more targeted patterns for known token formats (JWT, API keys) or add character class validation. 3. Missing Test Coverage - CRITICAL There are NO tests for the RedactedDebugOutput class. This security-critical feature absolutely needs test coverage for:
Potential Issues4. Header Matching Efficiency (lib/core/controlplane_api_direct.rb:21) 5. Limited Safe Headers List 6. Regex Escaping (lib/core/controlplane_api_direct.rb:5) Security ConsiderationsPositive:
Concerns:
Code Quality & StylePositive:
Suggestions:
Action ItemsMust Fix Before Merge:
Should Fix: Nice to Have: ConclusionThis PR solves an important security problem but needs test coverage and has a critical bug. The << method must return self and the redaction pattern needs refinement. With these fixes, this will be a solid security improvement. Recommendation: Request changes for items 1-3, then approve after addressed. |
Code ReviewThanks for this security improvement! This PR addresses an important concern by redacting sensitive data from trace output. Here is my feedback: Strengths
Potential Issues1. Security: Incomplete redaction coverage (Critical) The current implementation has a significant gap at lib/core/controlplane_api_direct.rb:19: The code only redacts long strings (50+ chars) in non-header lines, but misses shorter sensitive tokens. Examples:
Recommendation: Consider also redacting common sensitive JSON keys like token, secret, password, apiKey, etc., regardless of length. 2. Security: Header case-sensitivity issue Line lib/core/controlplane_api_direct.rb:21 has a subtle bug with casecmp. While casecmp handles case-insensitivity correctly, there is a potential nil return issue. If match[1] contains non-ASCII characters, casecmp could return nil, causing zero? to raise an error. Recommendation: Use casecmp? (with ?) instead as it is more idiomatic and returns a boolean directly. 3. Missing safe headers Common safe headers that might be useful for debugging are missing from the whitelist:
These do not contain sensitive data and could help with debugging. Test Coverage (Critical Gap)The PR adds a new class but no tests for it. This is a security-critical feature that should be thoroughly tested. Recommendation: Add a spec file spec/core/redacted_debug_output_spec.rb with tests for:
Code Quality Suggestions
Security AssessmentOverall: This is a good security improvement, but the current implementation may provide a false sense of security due to gaps in redaction coverage. The 50-character threshold misses many real-world secrets. Recommendation: Consider a more comprehensive approach by redacting based on common secret patterns and values for known sensitive JSON keys. SummaryThis PR moves in the right direction but needs:
Please add tests before merging. Great work on improving security! |
862c328 to
5bfc4ce
Compare
| match = line.match(HEADER_REGEX) | ||
| return line.gsub(/[\w\-._]{50,}/, "[REDACTED]") unless match | ||
|
|
||
| SAFE_HEADERS.any? { |h| h.casecmp(match[1]).zero? } ? line : "#{match[1]}: [REDACTED]\n" |
There was a problem hiding this comment.
Using .casecmp(x).zero? is a Ruby antipattern because String#casecmp returns nil (not 0) when the two strings have incompatible encodings, causing a NoMethodError at runtime. Prefer casecmp? which returns a boolean:
| SAFE_HEADERS.any? { |h| h.casecmp(match[1]).zero? } ? line : "#{match[1]}: [REDACTED]\n" | |
| SAFE_HEADERS.any? { |h| h.casecmp?(match[1]) } ? line : "#{match[1]}: [REDACTED]\n" |
| match = line.match(HEADER_REGEX) | ||
| return line.gsub(/[\w\-._]{50,}/, "[REDACTED]") unless match | ||
|
|
||
| SAFE_HEADERS.any? { |h| h.casecmp(match[1]).zero? } ? line : "#{match[1]}: [REDACTED]\n" |
There was a problem hiding this comment.
Net::HTTP sends raw wire bytes through the debug output, so header lines use CRLF terminators (\r\n). When msg.lines splits on \n, each line retains a trailing \r — meaning (.+) in the regex captures "Bearer token\r", not "Bearer token".
The redacted replacement uses \n only, so safe headers (returned unchanged) get \r\n while redacted headers get \n, producing mixed line endings in the output. This is harmless for display purposes but worth being aware of. If you want to preserve the original terminator:
def redact_line(line)
match = line.match(HEADER_REGEX)
return line.gsub(/[\w\-._]{50,}/, "[REDACTED]") unless match
terminator = line.end_with?("\r\n") ? "\r\n" : "\n"
SAFE_HEADERS.any? { |h| h.casecmp?(match[1]) } ? line : "#{match[1]}: [REDACTED]#{terminator}"
end|
|
||
| def redact_line(line) | ||
| match = line.match(HEADER_REGEX) | ||
| return line.gsub(/[\w\-._]{50,}/, "[REDACTED]") unless match |
There was a problem hiding this comment.
The body-redaction regex [\w\-._]{50,} catches JWT and cpln profile token output (which use base64url encoding with A-Za-z0-9-_), but misses tokens using standard base64 alphabet (+, /, =). For example, an AWS-style secret key or a base64-encoded blob in a response body would slip through.
If any response bodies might contain standard-base64 secrets, consider extending the character class:
line.gsub(/[A-Za-z0-9\-._+\/=]{50,}/, "[REDACTED]")This is a design trade-off — the wider class increases false-positive redactions in body JSON (e.g. long base64-encoded file content). Worth documenting the reasoning for the chosen character class either way.
| @@ -1,5 +1,27 @@ | |||
| # frozen_string_literal: true | |||
|
|
|||
| class RedactedDebugOutput | |||
There was a problem hiding this comment.
RedactedDebugOutput is a top-level class defined in controlplane_api_direct.rb. Since it is only used to wrap the debug output of ControlplaneApiDirect, nesting it as a private inner class (ControlplaneApiDirect::RedactedDebugOutput) or moving it to its own file would make the dependency explicit and avoid polluting the top-level namespace.
Review: Redact sensitive data from trace outputThe whitelist approach is the right call here — defaulting to redacting all headers except known-safe ones is much harder to get wrong than a blacklist. The overall direction is good. A few issues worth addressing before merge: Missing tests (most important)
|
Summary
RedactedDebugOutputclass to filter sensitive data from--traceoutputBefore
After