Skip to content
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

Fix a bug where the default HeadersSanitizer does not work in logging decorators #5519

Merged
merged 3 commits into from Apr 2, 2024

Conversation

ikhoon
Copy link
Contributor

@ikhoon ikhoon commented Mar 19, 2024

Motivation:

HeadersSanitizer was added in #5188. If no HeadersSanitizer is set, the default sanitizer masks Authorization, Cookie Set-Cookie, and Proxy-Authorization with ****.

In addition to HeadersSantizer, other new features were added to LoggingDecoratorBuilder and the code became messy to maintain backward compatibility. LoggingDecoratorBuilder, LogWriterBuilder, and LogFormatterBuilder each have their own properties, so making the overall operation consistent was not easy.

As a result, while LoggingClient.newDecorator() sanitizes sensitive headers, LoggingClient.builder().requestLogLevel(...).newDecorator()` did not work. Neither code set a sanitizer, so a default sanitizer should have been set, but it didn't.

Modifications:

  • Refactor LoggingDecoratorBuilder to delegate all builder properties to either LogWriterBuilder or LogFormatterBuilder.
  • Add missing @Deprecated annotation to LoggingClientBuilder and LoggingRpcClientBuilder and LoggingServiceBuilder.
  • Breaking) Change the signature of LogWriterBuilder.responseCauseFilter() to have RequestContext as the first parameter.
    • It may be a bug or a mistake when adding the API. Our APIs usually provide RequestContext for predicates or handlers.

Result:

LoggingClient and LoggingService now correctly mask sensitive headers by default.

Motivation:

`HeadersSanitizer` was added in line#5188. If no `HeadersSanitizer` is set,
the default santizer masks `Authorization`, `Cookie` `Set-Cookie` and
`Proxy-Authorization` with `****`.

In addition to `HeadersSantizer`, other new features are added to
`LoggingDecoratorBuilder` and the code became messy to maintain
backward compatibility. `LoggingDecoratorBuilder`, `LogWriterBuilder`,
and `LogFormatterBuilder` each have their own properties, so it was not
easy to make the overall operation consistent.

As a result, while `LoggingClient.newDecorator()` santizes sensitive
headers, LoggingClient.builder().requestLogLevel(...).newDecorator()`
did not work. Neither code set a sanitizer, so a default sanitizer
should have been set, but it didn't.

Modifications:

- Refactor `LoggingDecoratorBuilder` to delegate all builder properties
  to either `LogWriterBuilder` or `LogFormatterBuilder`.
- Add missing `@Deprecated` annotation to `LoggingClientBuilder` and
  `LoggingRpcClientBuilder` and `LoggingServiceBuilder`.
  - `@Deprecated` in the super method aren't inspected by IntelliJ line#5395
- Breaking) Change the signature of `LogWriterBuilder.responseCauseFilter()`
  to have `RequestContext` as the first parameter.
  - It may be a bug or a mistake when adding the API. Our APIs
    usually provide `RequestContext` for predicates or handlers.

Result:

`LoggingClient` and `LoggingService` now correctly mask sensitive
headers by default.
@ikhoon ikhoon added the defect label Mar 19, 2024
@ikhoon ikhoon added this to the 1.28.0 milestone Mar 19, 2024
@ikhoon ikhoon changed the title Fix a bug where the default HeadersSanitizer in logging decorators Fix a bug where the default HeadersSanitizer does not work in logging decorators Mar 19, 2024
Copy link

codecov bot commented Mar 19, 2024

Codecov Report

Attention: Patch coverage is 82.05128% with 21 lines in your changes are missing coverage. Please review.

Project coverage is 74.07%. Comparing base (c960d23) to head (cd0203c).

Files Patch % Lines
...rmeria/common/logging/LoggingDecoratorBuilder.java 80.19% 15 Missing and 5 partials ⚠️
...rmeria/client/logging/LoggingRpcClientBuilder.java 50.00% 1 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff             @@
##             main    #5519       +/-   ##
===========================================
+ Coverage        0   74.07%   +74.07%     
- Complexity      0    20807    +20807     
===========================================
  Files           0     1801     +1801     
  Lines           0    76558    +76558     
  Branches        0     9749     +9749     
===========================================
+ Hits            0    56708    +56708     
- Misses          0    15233    +15233     
- Partials        0     4617     +4617     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

}

@Override
public String toString() {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed this since we reached a consensus that toString() is not a mandatory implementation for builder classes.

Copy link
Contributor

@jrhee17 jrhee17 left a comment

Choose a reason for hiding this comment

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

Left some nits, but the fix looks solid 👍 Thanks @ikhoon ! 🙇 👍 🙇

@@ -176,7 +186,8 @@ private ResponseLogLevelMapper responseLogLevelMapper() {
* You can prevent logging the response cause by returning {@code true}
* in the {@link Predicate}. By default, the response cause will always be logged.
*/
public LogWriterBuilder responseCauseFilter(Predicate<? super Throwable> responseCauseFilter) {
public LogWriterBuilder responseCauseFilter(
Copy link
Contributor

Choose a reason for hiding this comment

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

Optional) I think we can avoid breaking changes by maintaining both methods ((ctx, t) -> bool, (t) -> (bool)).

Having said this, I've searched for usages and I think there aren't any usages so far

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I thought changing was okay since the API is unstable and was added recently.

@@ -42,12 +45,14 @@ public final class LogWriterBuilder {
static final ResponseLogLevelMapper DEFAULT_RESPONSE_LOG_LEVEL_MAPPER =
ResponseLogLevelMapper.of(LogLevel.DEBUG, LogLevel.WARN);

private Logger logger = DefaultLogWriter.defaultLogger;
@Nullable
private Logger logger;
Copy link
Contributor

Choose a reason for hiding this comment

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

I understood this change is just a change of code style and there is no difference in functionality

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@@ -235,8 +214,9 @@ public LoggingDecoratorBuilder responseLogLevel(Class<? extends Throwable> clazz
*/
@Deprecated
public LoggingDecoratorBuilder successfulResponseLogLevel(LogLevel successfulResponseLogLevel) {
requireNonNull(successfulResponseLogLevel, "successfulResponseLogLevel");
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you check the missing rnn in this class overall?

Copy link
Contributor Author

@ikhoon ikhoon Mar 21, 2024

Choose a reason for hiding this comment

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

It was delegated to logWriterBuilder. Do you think we also need to add null-checking here?

Copy link
Contributor

Choose a reason for hiding this comment

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

I see, I guess I missed it. Thanks for the explanation 👍

protected final BiFunction<? super RequestContext, ? super Throwable, ? extends @Nullable Object>
responseCauseSanitizer() {
return responseCauseSanitizer;
return null;
Copy link
Contributor

Choose a reason for hiding this comment

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

Question) Why do we handle this method differently from the other methods?

Suggested change
return null;
if (logFormatterBuilder != null) {
return logFormatterBuilder.responseContentSanitizer();
}
return null;

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I figured out that responseCauseSanitizer was not used to sanitize an exception.
responseCauseFilter was actually used and responseCauseSanitizer was only referenced by toString() method.

Copy link
Member

@minwoox minwoox left a comment

Choose a reason for hiding this comment

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

Thanks for the clean up! 👍 👍 👍

@jrhee17 jrhee17 merged commit 42edbb2 into line:main Apr 2, 2024
18 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants