Java Analyzer is now over 2.5X faster

In the past quarter, we have improved our Java Analyzer significantly. In this release, you’ll see a sturdier Java analyzer, coverage for new issues, and various false-positive fixes. Scroll down for all the details.

Faster, more reliable analysis

We have a new back-end for the Java Analyzer now, which is faster and more reliable than ever. With this, we have managed to improve the speed of Java analyses by over 2.5 times. Java analysis timeouts are also a thing of the past now.

How did we do this?

In its beta days, the Java Analyzer used to build your projects and run bytecode analysis on the compilation results. This worked out about as well as anyone could have expected it to, and used to time out in many cases if your project took too long to compile.

We now directly analyze Java code by creating an Abstract Syntax Tree (AST) for each file, inspecting the AST, and extracting information regarding classes, variables, and methods. This is essentially the beginning of what javac does. Since we don’t need to worry about generating bytecode, we can be even more fault-tolerant during analysis as well. The new Java Analyzer is very difficult to crash, (It even shrugged off an OutOfMemoryException once while testing it) and is much faster than the older runtime, which we are calling the “legacy” runtime now.

Legacy mode

Since we are in the process of migrating all the issues previously detected by the analyzer to the new runtime, you might notice that some issues are not being raised anymore. To remedy this, we have introduced legacy mode, which enables old Java Analyzer beta features. This mode will likely turn up more issues but may result in slower analysis with a higher chance of timeouts occurring. We are actively working on migrating all the issues to the new runtime.

You can enable legacy mode on your repo by modifying the Java section of your .deepsource.toml file to add the legacy flag:

version = 1

[[analyzers]]
name = "java"
enabled = true

  [analyzers.meta]
  runtime_version = 11
  legacy = true

Note: We highly recommend not using Legacy mode to have a better experience. Legacy mode will be deprecated once we finish migrating all issues. Usage of this flag in your config will be ignored upon deprecation.

Other additions and improvements

New issues

We’ve brought in a number of new issues, including an assortment of security issues; here’s a sampler:

JAVA-E0394: Regex strings must be valid

This issue will trigger if Pattern.compile() and other related methods are called with an invalid regex string.

Pattern regex = Pattern.compile("(////)"); // This is an invalid regex in Java.

JAVA-S1004: CBC and ECB modes are insecure

CBC (Cipher Block Chaining) is a mode of block-based encryption which uses previously encrypted blocks as input when encrypting the next plain-text block. CBC encryption is flawed due to its vulnerability to the Padding Oracle Attack.

ECB is an encryption mode that is even more broken and renders data encrypted with it exposed to a number of cryptographic attacks.

This issue will be raised if usage of CBC or ECB mode is detected.

Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding"); // Susceptible to padding oracle attacks.
Cipher d = Cipher.getInstance("AES"); // The default mode if no mode is specified is ECB.

JAVA-S1009: Instances of XMLStreamReader should be secure

Instances of the XMLStreamReader class are created through the XMLInputFactory API, which also allows one to set input processing options for any created XMLStreamReaders. If the correct flags are not set, and untrusted data is treated as an XML stream, it is possible that a security breach could occur via specially crafted XML payloads. When using XMLInputFactory, the IS_SUPPORTING_EXTERNAL_ENTITIES and/or SUPPORT_DTD flags must be set to false to prevent side-channel attacks.

This issue will be raised if the analyzer detects that any of these flags are not set to false.

InputStream input = ...;
XMLInputFactory factory = XMLInputFactory.newFactory();
XMLStreamReader reader = factory.createXMLStreamReader(input); // we have set no flags here.

False-positive fixes

We’ve resolved false positives for the following issues:

JAVA-W0324: Private method is never called

This issue was earlier mistakenly reported if a private method was used in a nested class. This is no longer the case, and private methods defined within a class and used within nested classes will not be reported as unused.

class WithNested {

	private void method() { ... } // This method will not be reported.

	class Nested {
		void method2() {
			WithNested.this.method();
		}
	}
}

Also, this issue will no longer be reported on methods marked with JUnit test annotations such as @Test and @BeforeEach. Annotations from dependency injection frameworks like @Inject and @Autowired as well as annotations from the Spring JPA library, such as @PreUpdate are also supported.

@Autowired
public void someComponentMethod(...) { ...}

Another case where this issue was mistakenly reported for private methods that were used as method references is also fixed:

public class PrivateMethodReference {

  public void method() {
    Lists.of(1, 2, 3, 4, 5)
	.stream()
	.map(this::privateMethod) // the privateMethod method will correctly be recognised as being used now.
	.forEach(System.out::println);
  }

  private String privateMethod(int val) {
    return "number " + val;
  }
}

Additionally, several edge cases present during the detection of this issue have been fixed.

JAVA-W0100: equals was not overridden from superclass

This issue will no longer falsely report enumerations since they are not supposed to explicitly implement equals.

JAVA-W0280: == should not be used to compare objects

This issue was wrongly reported within equals methods, even ones auto-generated by IDEs such as IntelliJ IDEA which may be well in their rights to perform reference comparisons. DeepSource will no longer report such valid usages.

JAVA-P0062: Inefficient use of String constructor

This issue was incorrectly raised for any usage of the String() constructor, rather than only for strings constructed from string literals (new String("abc") for example).

That’s all for now, but keep checking back; we’re working on even more cool things!

3 Likes