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

[improvement] Enforce guava and objects calls with no parameters and constant strings use logsafe #517

Merged
merged 3 commits into from
Jan 24, 2019

Conversation

dsd987
Copy link
Contributor

@dsd987 dsd987 commented Jan 21, 2019

Before this PR

There is no standardization in the use of basic calls to verify conditions like guava's Preconditions.check* or Objects.requireNonNull.

After this PR

Simple use cases of guava Preconditions.check* and Objects.requireNonNull are now required to be changed to use logsafe variants. This will help to standardize calls where it is clear there can be a direct one-for-one replacement made with logsafe calls.

@dsd987 dsd987 requested a review from a team as a code owner January 21, 2019 20:51
@dsd987
Copy link
Contributor Author

dsd987 commented Jan 21, 2019

To address #507

@@ -62,8 +75,6 @@ public final void validGuava() throws Exception {
"class Test {",
" private static final String compileTimeConstant = \"constant\";",
" void f(boolean bArg, int iArg, Object oArg) {",
" Preconditions.checkArgument(bArg);",
" Preconditions.checkArgument(bArg, \"message\");",
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think this should be modified, it is valid for the existing test, just not the new check


@AutoService(BugChecker.class)
@BugPattern(
name = "GuavaPreconditionsEnforceLogSafeUse",
Copy link
Contributor

Choose a reason for hiding this comment

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

We're really checking one thing: functionality that can be replaced with safe-logging utilities without losing any information/utility. I'd prefer to have a single check for that.

Based on the existing PreferSafeLoggableExceptions check we should call it PreferSafeLoggingPreconditions.

@BugPattern(
name = "GuavaPreconditionsEnforceLogSafeUse",
category = BugPattern.Category.ONE_OFF,
severity = BugPattern.SeverityLevel.ERROR,
Copy link
Contributor

Choose a reason for hiding this comment

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

Based on PreferSafeLoggableExceptions we should use SeverityLevel.SUGGESTION by default, in certain large internal codebases we will override it to ERROR. Longer term we should set the new check and PreferSafeLoggableExceptions to error, but I'd like to decouple that from this change.


if (args.size() == 2) {
ExpressionTree messageArg = args.get(1);
if (!compileTimeConstExpressionMatcher.matches(messageArg, state)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

This needs to check the type of args[1], otherwise we'll get false positives on Objects.requireNonNull(obj, () -> calculateExpensiveMessage()) using the supplier overload.

category = BugPattern.Category.ONE_OFF,
severity = BugPattern.SeverityLevel.ERROR,
summary = "Objects.requireNonNull(Object) and Objects.requireNonNull(Object, String) methods with a "
+ "constant message and no parameters should use logsafe Preconditions.checkNotNull(Object, String)")
Copy link
Contributor

Choose a reason for hiding this comment

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

might as well give the FQN: com.palantir.logsafe.Preconditions#checkNotNull(Object, String)

Copy link
Contributor

Choose a reason for hiding this comment

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

Also I'd recommend giving a little hint as to why error-prone is recommending the change, e.g. the PreferSafeLoggableExceptions rule says "Throw SafeLoggable exceptions to ensure the exception message will not be redacted"

Matchers.anyOf(
MethodMatchers.staticMethod()
.onClass("com.google.common.base.Preconditions")
.withNameMatching(Pattern.compile("checkArgument|checkState|checkNotNull")));
Copy link
Contributor

Choose a reason for hiding this comment

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

Probably a separate PR, but would be good to check Guava’s Verify.verify & Verify.verifyNotNull as well as commons-lang Validate.*?

Copy link
Contributor

Choose a reason for hiding this comment

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

That would be difficult -- guava Preconditions throws standard IllegalState/IllegalArg/NullPointer exceptions, but these methods throw library-specific exception types, so a migration could break existing catches.

I think we should suggest against using them in favor of safe-logging preconditions, but I'd prefer to do so in a different check.

Copy link
Contributor

Choose a reason for hiding this comment

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

👍 Makes sense for Guava's Verify and I expect much lower usage of that.

The commons-lang Validate is much more heavily used in some places, and those should be straightforward replacements by equivalent Safe preconditions

Copy link
Contributor

Choose a reason for hiding this comment

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

I had though lang3 validate used custom exception types, thanks for the correction! In that case it makes sense to add common methods to this check. Let's add it in a separate PR.

@dsd987 dsd987 force-pushed the dd/enforceLogSafe branch 2 times, most recently from f484ec9 to 36f119a Compare January 23, 2019 16:04
.replace(tree, String.format(
"com.palantir.logsafe.Preconditions.checkNotNull(%s)", tree.getArguments()))
.build())
.build();
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we combine these? The message is different, but doesn't need to be because we output a suggested change which illustrates the current and proposed states. We can add a function which maps from a given method name to a safe-logging Preconditions method name instead.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

👍


if (args.size() == 2) {
ExpressionTree messageArg = args.get(1);
if (!messageArg.getKind().equals(Tree.Kind.STRING_LITERAL)
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we actually want to test that this is a string literal? Will that catch references to compile-time constant methods?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good catch! Yeah, that won't work with compile-time constants. Will fix.

if (args.size() == 2) {
ExpressionTree messageArg = args.get(1);
if (!messageArg.getKind().equals(Tree.Kind.STRING_LITERAL)
|| !compileTimeConstExpressionMatcher.matches(messageArg, state)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we should check the method matchers prior to this, I imagine the compile time constant matcher is more expensive than checking method names, and this will fire for all 2-arg methods with a string as the second argument.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

👍

@carterkozak
Copy link
Contributor

This check should be added to the list of exclusions for gradle plugins, since those don't use safe-logging

@dsd987 dsd987 force-pushed the dd/enforceLogSafe branch from 36f119a to 8a31435 Compare January 23, 2019 19:14
README.md Outdated
- `PreferSafeLoggingPreconditions`: Users should use the safe-logging versions of Precondition checks for standardization when there is equivalent functionality
```diff
-com.google.common.base.Preconditions.checkNotNull(variable, "message");
-com.palantir.logsafe.Preconditions.checkNotNull(variable, "message"); // equivalent functionality is available in the safe-logging variant
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Should be a "+"

@carterkozak
Copy link
Contributor

Please don't force-push, that makes it hard to review diffs!

Copy link
Contributor

@iamdanfox iamdanfox left a comment

Choose a reason for hiding this comment

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

Looks solid to me :) I'm looking forward to actually exposing those suggested fixes!

Copy link
Contributor

@carterkozak carterkozak left a comment

Choose a reason for hiding this comment

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

lgtm, thanks @dsd987!

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.

4 participants