Using SonarQube to Analyze a Java Project (Part 2)
In the previous article on that subject, we walked through a working setup of SonarQube to analyze the Apache James source code. In this shorter article, we continue with two more advanced features and eventually discuss how this initiative benefited James.
OWASP Dependency Check
One can do its best to make their own codebase flawless and still be defeated due to their use of libraries. Many if not all software projects require external libraries, especially in languages like Java. Obviously, there is little one can do to ensure that the libraries they are currently using do not contain unknown vulnerabilities. However, the minimum would be to ensure that those libraries do not contain known and already fixed vulnerabilities. In other words, libraries should be up-to-date. The problem that will then arise is that it is not conceivable to constantly be running the latest version of every single used library, in particular when dozens of them are used: new versions can bring new issues, break APIs, etc. As a consequence, you usually need to “follow the news” and upgrade libraries as soon as a new CVE is disclosed. This can be tricky, takes a lot of time and you can easily miss some and keep running a vulnerable library for a long time.
The OWASP Dependency Check was specifically developed for this purpose: it identifies project dependencies and checks whether there are any known, publicly disclosed, vulnerabilities. Running it is easy and a SonarQube plugin exists for it. This is precisely the JAR file we talked about in the first article and that was automatically put in the right directory, thus it is already installed. You can see that with the
Dependency-Check tab in the general settings:
However, this plugin is only an interface. Dependency-Check can either be run as a CLI tool or through various plugins for Maven, Ant, Jenkins, etc. In this article, we will use the CLI tool. You can download the ZIP file here.
After having unzipped it, we can run the
bin/dependency-check.sh script. We choose the XML output file format (HTML is also compatible with SonarQube) and restrict the scan to the JAR files located in libraries directories ; otherwise, a huge number of false positives with no meaning will appear. Here is the final command:
root@server:~/dependency-check# bin/dependency-check.sh \
> --format XML --out .. --project "apache-james" \
> --scan '../james-project/**/*.lib/*.jar'
The first run takes more time because Dependency-Check needs to fill its local CVE database, but potential following runs will be much quicker.
Now, we need to rerun a SonarQube analysis with Maven specifying the location of the previously generated report file as a new property, named
sonar.DependencyCheck.reportPath (because we chose the XML file format, otherwise the property name is
sonar.DependencyCheck.htmlReportPath). As seen in the first article, we can either set this property in our
pom.xml file or use a command-line option.
Note that we could also use Docker volumes to make the report directly available in the SonarQube container and then specify the path to it from the web interface, in the
Dependency-Check tab of the General Settings.
Let’s now take a look at the new vulnerabilities that were reported by SonarQube based on the OWASP Dependency Check analysis:
The rule we are interested in here is “Using Components with Known Vulnerabilities”. In our case, more than 24 000 issues are reported. However, due to the way Dependency-Check works (with heuristics, string matching, CPEs, etc.), there are a lot of false positives and duplications.
First, we need to exclude our own libraries from the analysis. Indeed, we take for granted that we know about CVEs for these. Plus, they would be reported as vulnerable if they depended on other vulnerable libraries, which would themselves be reported separately. Then, after having excluded all libraries containing the “james” string in their name, we can go through the results and start with the
Critical issues since they are the ones with relatively high CVE scores.
For each reported issue, we need to check the corresponding CVE, for instance here and here, and look if our library version is really affected by it. Often, it is simply a mismatch and we can then remove this particular library from the list of scanned ones using the
—exclude command-line option, which can be repeated and accepts Ant style exclusions. If, on the contrary, our library is vulnerable, we write down somewhere that we will need to upgrade it to a patched version and then remove it from the list as well. By doing so, we manage to quickly decrease the amount of reported issues.
I recognize this is not elegant. There may be better ways to do that, but I eventually chose to do it this way. We had more than 60 000
Critical issues at first and finally got down to only three libraries that required to be upgraded. It only took me a few hours to do all that research.
A Quality Gate is basically a set of conditions a project must meet before it can be released into production. In other words, it is a way to enforce a quality policy for different projects. The default one is named
SonarQube way and we can take a look at the conditions it uses:
For instance, we can see that the analysis of a project that uses the SonarQube way quality gate will be flagged as failed if the coverage of new code is below 80 %. Generally speaking, a quality gate is useful to provide developers with a quick view of the evolution of a project.
We can create a new quality gate (or copy an existing one) from the
Quality Gates administration section and then assign it to an existing project. Then, we have the possibility to write as many conditions as we want:
Conditions can be related to code coverage, duplications, issues (number, severity, status, etc.), maintainability and many more types of metrics. You can learn more about that in the documentation.
Benefits for Apache James
Using SonarQube in a similar manner to what was described in those two articles, we eventually found and fixed a few security issues in James:
- Various uses of weak hash functions: some parts of James were using the MD5 or SHA-1 hash functions, which are more prone to collisions and thus allowed, depending on the cases, an attacker to potentially overwrite some sensitive data. Those were replaced with the more reliable SHA-256 function.
- Various uses of not cryptographically strong Random Number Generators (RNGs): some parts of James are relying on supposedly random numbers, for instance when dealing with IDs or generating unique names. If the RNGs used produce a predictable output, an observing attacker could manage to obtain or use a particular value to overwrite things that should not be, such as files or mailboxes. Although rather unlikely, those scenarios are conceivable and easy to avoid by using better cryptographically robust RNGs.
- LDAP injections: some user inputs were not correctly sanitized before being used as part of filters for LDAP search queries. This could for instance be useful to bruteforce user passwords more easily. We fixed this by escaping LDAP special characters in LDAP filters.
In this second part, we mainly dealt with the OWASP Dependency Check, quickly addressed Quality Gates and pointed out the benefits that the use of SonarQube recently had on Apache James. We hope that it will help developers provide an ever more secure email server.