Error Prone Documentation
repository·master·Indexed 27 days ago
https://github.com/google/error-proneError Prone is a static analysis tool for Java that catches common programming mistakes at compile-time by hooking into the Java compiler. It supports integration with Bazel, Maven, Ant, and Gradle. The tool provides a variety of bug patterns to identify logic errors, security vulnerabilities (such as Fragment Injection in PreferenceActivity), and Android-specific issues like unsafe Bundle deserialization casts and Binder identity management.
What's inside Error Prone
- Error Prone is a static analysis tool for Java designed to catch common programming mistakes at compile-time. It integrates into the compilation process to identify bugs that standard compilers might miss, providing descriptive error messages that point to the specific cause of the issue.
Understand the MoreThanOneInjectableConstructor bug pattern
masterThe
MoreThanOneInjectableConstructorbug pattern identifies classes that have multiple constructors annotated with@Inject.Injection frameworks use the
@Injectannotation to determine which constructor to use when constructing an object. If a class contains more than one constructor with this annotation, the framework cannot reliably choose between them, which can lead to unpredictable behavior or runtime errors.Understand the @RequiredModifiers annotation
masterThe@RequiredModifiersannotation is used to enforce that specific modifiers must be present on an element. If you attempt to apply an annotation that is itself annotated with@RequiredModifiersto an element missing the required modifiers, Error Prone will trigger an error.Identify insecure cryptographic algorithm usage with InsecureCryptoUsage
masterThe
InsecureCryptoUsagechecker flags three specific classes of cryptographic vulnerabilities in Java code:Insecure Cipher Configurations:
- Creating
javax.crypto.Cipherinstances using default settings or the insecure ECB mode. - Note that
Cipher.getInstance(AES)defaults to ECB mode in Java. - Dynamically constructed transformation strings are flagged to prevent concealing ECB mode.
- IES-based cipher algorithms are flagged because their implementations use ECB mode.
- Creating
Insecure Diffie-Hellman (DH) Implementations:
- Flags any operation involving the Diffie-Hellman protocol on prime fields, as these are often exploitable.
- Recommendation: Use Elliptic Curve Diffie-Hellman (ECDH) instead, as it is considered secure.
Insecure DSA Signatures:
- Flags all cryptographic operations involving DSA (Digital Signature Algorithm) due to vulnerabilities in certain library configurations that accept invalid signatures.
Understand the MissingRefasterAnnotation bug pattern
masterThe
MissingRefasterAnnotationbug pattern identifies Refaster templates that contain methods missing required annotations. A valid Refaster template typically consists of multiple methods where each method is decorated with an annotation (such as@BeforeTemplateor@AfterTemplate). If a method within a template class lacks an annotation, it is flagged as an oversight.static final class MethodLacksBeforeTemplateAnnotation { @BeforeTemplate boolean before1(String string) { return string.equals(""); } // @BeforeTemplate is missing boolean before2(String string) { return string.length() == 0; } @AfterTemplate @AlsoNegation boolean after(String string) { return string.isEmpty(); } }Understand the BanJNDI bug pattern
masterThe
BanJNDIbug pattern identifies and bans usage of Java JDK APIs that can result in the deserialization of unsafe objects via the Java Naming and Directory Interface (JNDI).JNDI allows Java objects to be serialized and deserialized over the wire. If an application performs a JNDI lookup over a transport protocol, a remote server can execute arbitrary attacker-defined code.
This checker works in two ways:
- Direct Ban: It bans specific high-risk APIs.
- Indirect Ban: It bans specific methods (
lookup(),bind(),rebind(),getAttributes(),modifyAttributes(),createSubcontext(),getSchema(),getSchemaClassDefinition(), andsearch()) on any subclass or implementer ofjavax.naming.Context. This prevents attackers from bypassing the check by casting a vulnerable implementation to the baseContexttype.
Identify SizeGreaterThanOrEqualsZero bug pattern
masterTheSizeGreaterThanOrEqualsZerobug pattern detects logic errors where a developer checks if the size of an array or collection is greater than or equal to 0. Since the size of a collection is always at least 0, this check is always true and typically indicates a mistake where the developer intended to check if the collection was non-empty (i.e.,size > 0).Understand the RedundantNullCheck bug pattern
masterThe
RedundantNullCheckpattern flags null checks (e.g.,x == nullorx != null) performed on expressions that are statically determined to be non-null. This occurs when:- Language semantics guarantee the value is non-null.
- The code is within a
@NullMarkedscope and the type is not explicitly annotated with@Nullable.
This helps clean up code and reinforces the contracts provided by nullness annotations.
import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; @NullMarked class MyClass { void process(String definitelyNonNull) { // BUG: RedundantNullCheck if (definitelyNonNull == null) { System.out.println("This will never happen"); } } String getString() { return "hello"; } @Nullable String getNullableString() { return null; } void anotherMethod() { String s = getString(); // BUG: RedundantNullCheck (s is known to be non-null) if (s == null) { System.out.println("Redundant check"); } String nullableStr = getNullableString(); if (nullableStr == null) { // This check is NOT redundant System.out.println("Nullable string might be null"); } } }Refactor null checks to use requireNonNull
masterTo improve code readability and potentially gain minor performance benefits, replace manual null checks that throw a
NullPointerExceptionwithjava.util.Objects.requireNonNull(value).Manual pattern to avoid:
if (value == null) { throw new NullPointerException(...); }Recommended pattern:
java.util.Objects.requireNonNull(value);Benefits of
requireNonNull:- Readability: It is more concise and expressive.
- Performance: It is annotated with
@ForceInlineand receives special JVM treatment to ensure it is inlined into equivalentif/throwcode. It also produces slightly smaller bytecode, which can assist JIT inlining decisions.
Use Pattern Matching for instanceof
masterError Prone identifies code patterns where
instanceofis used followed by an explicit cast to the same type. You can refactor these patterns to use Java's pattern matching forinstanceofto make the code more concise and readable. This eliminates the need for a separate casting step.// Refactor this: void handle(Object o) { if (o instanceof Point) { Point point = (Point) o; handlePoint(point.x(), point.y()); } else if (o instanceof String) { String s = (String) o; handleString(s); } } // To this: void handle(Object o) { if (o instanceof Point(int x, int y)) { handlePoint(x, y); } else if (o instanceof String s) { handleString(s); } }Avoid unbounded work queues in ThreadPoolExecutor
masterThe
ErroneousThreadPoolConstructorCheckerbug pattern identifies cases where aThreadPoolExecutoris constructed with an unboundedworkQueue. When the queue is unbounded, the pool size will never exceed thecorePoolSize, rendering themaximumPoolSizeparameter ineffective.To fix this, you should either:
- Set
corePoolSizeequal tomaximumPoolSizeif you intend to use an unbounded queue. - Use a bounded queue (e.g.,
new LinkedBlockingQueue<>(capacity)) to allow the pool to scale up to themaximumPoolSizewhen the queue fills up.
// Bad: maximumPoolSize is ignored because the queue is unbounded new ThreadPoolExecutor( /* corePoolSize= */ 1, /* maximumPoolSize= */ 10, /* keepAliveTime= */ 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>()); // Good: corePoolSize and maximumPoolSize are equal new ThreadPoolExecutor( /* corePoolSize= */ 10, /* maximumPoolSize= */ 10, /* keepAliveTime= */ 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>()); // Good: Using a bounded queue allows scaling to maximumPoolSize new ThreadPoolExecutor( /* corePoolSize= */ 1, /* maximumPoolSize= */ 10, /* keepAliveTime= */ 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>(QUEUE_CAPACITY));- Set
Avoid non-distinct arguments in varargs methods with DistinctVarargsChecker
masterTheDistinctVarargsCheckeridentifies usage of varargs methods where the provided arguments are not distinct. Using non-distinct arguments in certain methods can lead to redundant operations or trigger runtime exceptions, such asIllegalArgumentException.